package org.simplextensions.scanner.impl;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.simplextensions.scanner.ClasspathScannerException;
import org.simplextensions.scanner.IClasspathScannerBundle;
import org.simplextensions.scanner.IClasspathScannerBundle.AClassFileIterator;

import javassist.bytecode.ClassFile;

/**
 * 
 * @author Tomasz Krzyzak, <a
 *         href="mailto:tomasz.krzyzak@gmail.com">tomasz.krzyzak@gmail.com</a>
 * @since 31-08-2010 21:21:23
 */
public class DirClasspathScannerBundle implements IClasspathScannerBundle {

	private final File dir;

	private class DirClassFileIterator extends AClassFileIterator {

		private Queue<File> files2Process = new ConcurrentLinkedQueue<File>();

		public DirClassFileIterator(Set<String> classNamesFilter) {
			super(classNamesFilter);
			this.files2Process.add(dir);
		}

		protected ClassFile searchNext() {
			ClassFile result = null;

			while (result == null && !files2Process.isEmpty()) {
				File polled = files2Process.poll();

				if (polled.isFile()) {
					if (polled.getName().endsWith(".class")) {
						InputStream inputStream = null;
						try {
							if (getClassNamesFilter().size() > 0) {
								String className = polled.getCanonicalPath().substring(dir.getCanonicalPath().length() + 1);
								className = className.substring(0, className.length() - (className.length() > 6 ? 6 : 0)).replace(
										File.separatorChar, '.');
								if (!getClassNamesFilter().contains(className)) {
									continue;
								}
							}
							inputStream = new FileInputStream(polled);
							result = new ClassFile(new DataInputStream(inputStream));
						} catch (Exception e) {
							throw new ClasspathScannerException("Cant open/read class file: " + polled.getAbsolutePath(), e);
						} finally {
							if (inputStream != null) {
								try {
									inputStream.close();
								} catch (IOException e1) {
									// no need to do anything
								}
							}
						}
					}
				} else {
					files2Process.addAll(Arrays.asList(polled.listFiles()));
				}
			}

			return result;
		}

	}

	public DirClasspathScannerBundle(File dir) {
		this.dir = dir;
	}

	public Iterator<ClassFile> classFileIterator(Set<String> classNamesFilter) {
		return new DirClassFileIterator(classNamesFilter);
	}

}
